﻿#include "demux_avi.h"
#include "mem.h"
#include "video_stream.h"

const FCC FCC_RIFF = { .id = 0X46464952 };	//RIFF
const FCC FCC_AVI_ = { .id = 0X20495641 };	//AVI
const FCC FCC_LIST = { .id = 0X5453494C };	//LIST
const FCC FCC_hdrl = { .id = 0X6C726468 };	//hdrl
const FCC FCC_strl = { .id = 0X6C727473 };	//strl
const FCC FCC_movi = { .id = 0X69766F6D };	//movi
const FCC FCC_avih = { .id = 0X68697661 };	//avih
const FCC FCC_strh = { .id = 0X68727473 };	//strh
const FCC FCC_strf = { .id = 0X66727473 };	//strf
const FCC FCC_strd = { .id = 0X64727473 };	//strd
const FCC FCC_vids = { .id = 0X73646976 };	//vids
const FCC FCC_auds = { .id = 0X73647561 };	//auds
const FCC FCC_JUNK = { .id = 0X4B4E554A };	//JUNK
const FCC FCC_00db = { .id = 0X62643030 };	//00db
const FCC FCC_01db = { .id = 0X62643130 };	//01db
const FCC FCC_00dc = { .id = 0X63643030 };	//00dc
const FCC FCC_01dc = { .id = 0X63643130 };	//01dc
const FCC FCC_00wb = { .id = 0X62773030 };	//00wb
const FCC FCC_01wb = { .id = 0X62773130 };	//01wb
const FCC FCC_MJPG = { .id = 0X47504A4D };	//MJPG
const FCC FCC_H264 = { .id = 0X34363248 };	//H264
const FCC FCC_ix00 = { .id = 0x30307869 };	//ix00
const FCC FCC_ix01 = { .id = 0x31307869 };	//ix01
const FCC FCC_idx0 = { .id = 0X30786469 };	//idx0
const FCC FCC_idx1 = { .id = 0X31786469 };	//idx1

const AVI_CONTEXT default_conxext =
{
	.avi_file_map =
	{
		.chunk_RIFF.fccid.id = 0X46464952,//RIFF
		.chunk_RIFF.size = 0,//byron 录音完成后回填
		.chunk_RIFF.fcctype.id = 0X20495641,//AVI

		.chunk_hdrl.fccid.id = 0X5453494C,//LIST
		.chunk_hdrl.size = sizeof(FCC) + sizeof(AVI_HEADER) + sizeof(VIDS_STRL) + sizeof(AUDS_STRL),
		.chunk_hdrl.fcctype.id = 0X6C726468,//hdrl

		.avi_header.avih.fccid.id = 0X68697661,//avih
		.avi_header.avih.size = sizeof(MainAVIHeader),	//0x38,

		.avi_header.mainaviheader = 
		{
			.dwMicroSecPerFrame = 1000000 / FPS,
			.dwMaxBytesPerSec = SAMPLERATE * SAMPLESIZE * CHANNELS,
			.dwReserved1 = 0, //PaddingGranularity = 0,
			.dwFlags = AVIF_TRUSTCKTYPE | AVIF_ISINTERLEAVED | AVIF_HASINDEX,
			.dwTotalFrames = 0,//byron 录音完成后回填
			.dwInitialFrames = 0,
			.dwStreams = 2,
			.dwSuggestedBufferSize = 0x00100000,
			.dwWidth = VIDEO_WIDTH,
			.dwHeight = VIDEO_HEIGHT,
			.dwScale = 0,
			.dwRate = 0,
			.dwStart = 0,
			.dwLength = 0,
		},
	
		.vids_strl =
		{
			.chunk_strl.fccid.id = 0X5453494C,//LIST
			.chunk_strl.size = sizeof(VIDS_STRL) - 8,
			.chunk_strl.fcctype.id = 0X6C727473,//strl

			.strh_vids.fccid.id = 0X68727473,//strh
			.strh_vids.size = sizeof(FCC) + sizeof(AVIStreamHeader),
			.strh_vids.fcctype.id = 0X73646976,//vids
			
			.vids_header = 
			{
				.fccHandler.id = 0X34363248,//H264
				.dwFlags = 0,
				.dwReserved1 = 0,	// u16 Priority;	u16 Language;
				.dwInitialFrames = 0,
				.dwScale = 1,
				.dwRate = FPS,
				.dwStart = 0,
				.dwLength = 0,	//byron 录音完成后回填
				.dwSuggestedBufferSize = 65536,
				.dwQuality = (u32)(-1),
				.dwSampleSize = 0,
				.u.v = 
				{
					.Left = 0,
					.Top = 0,
					.Right = VIDEO_WIDTH,
					.Bottom = VIDEO_HEIGHT,
				},
			},

			.item_strf.fccid.id = 0X66727473,//strf
			.item_strf.size = sizeof(BitmapInfoHeader),

			.bmpinfo = 
			{
				.biSize = sizeof(BitmapInfoHeader),
				.biWidth = VIDEO_WIDTH,
				.biHeight = VIDEO_HEIGHT,
				.biPlanes = 1,
				.biBitCount = 24,
				.biCompression = 0X34363248,//H264
				.biSizeImage = VIDEO_WIDTH * VIDEO_HEIGHT * 3,
				.biXPelsPerMeter = 0,
				.biYPelsPerMeter = 0,
				.biClrUsed = 0,
				.biClrImportant = 0,
			},

			.junk = 
			{
				.h.fccid.id = 0X4B4E554A,//JUNK
				.h.size = sizeof(JUNK) - 8,
				.unknow1 = 4,
				.unknow2 = 0,
				.fccFlagTag.id = 0X63643030,//00dc
				.dwReserved3 = {0,},
			},

			//.strd = {0,},
			//.strn = {0,},
			//.strx = {0,},
		},

		.auds_strl =
		{
			.chunk_strl.fccid.id = 0X5453494C,//LIST
			.chunk_strl.size = sizeof(VIDS_STRL) - 8,
			.chunk_strl.fcctype.id = 0X6C727473,//strl

			.strh_auds.fccid.id = 0X68727473,//strh
			.strh_auds.size = sizeof(FCC) + sizeof(AVIStreamHeader),
			.strh_auds.fcctype.id = 0X73647561,//auds

			.auds_header = 
			{
				.fccHandler.id = 1,
				.dwFlags = 0,
				.dwReserved1 = 0,	// u16 Priority;	u16 Language;
				.dwInitialFrames = 0,
				.dwScale = 1,
				.dwRate = SAMPLERATE,
				.dwStart = 0,
				.dwLength = 0,	//byron 录音完成后回填
				.dwSuggestedBufferSize = AUDIO_FRM_SIZE,
				.dwQuality = (u32)(-1),
				.dwSampleSize = SAMPLESIZE,
				.u.a =
				{
					.dwReserved2 = 0,
					.dwReserved2 = 0,
				},
			},
			
			.item_strf.fccid.id = 0X66727473,//strf
			.item_strf.size = sizeof(ExternWavFotmat),

			.wavformat = 
			{
				.wFormatTag = 1,
				.nChannels = CHANNELS,
				.nSamplesPerSec = SAMPLERATE,
				.nAvgBytesPerSec = SAMPLERATE * CHANNELS * SAMPLESIZE,
				.nBlockAlign = 2,
				.wBitsPerSample = 16,
				.cbSize = 14,
				.Reserve1 = 0,
				.Reserve2 = 0,
				.Reserve3 = 0,
				.Reserve4 = 0,
				.Reserve5 = 0,
			},

			.junk =
			{
				.h.fccid.id = 0X4B4E554A,//JUNK
				.h.size = sizeof(JUNK) - 8,
				.unknow1 = 4,
				.unknow2 = 0,
				.fccFlagTag.id = 0X62773130,//01wb
				.dwReserved3 = {0,},
			},

			//.strd = {0,},
			//.strn = {0,},
			//.strx = {0,},
		},

		.chunk_movi.fccid.id = 0X5453494C,//LIST
		.chunk_movi.size = 0,	//byron 录音完成后回填
		.chunk_movi.fcctype.id = 0X69766F6D,//movi

		.item_idx1.fccid.id = 0X31786469,//idx1
		.item_idx1.size = 0,	//byron 录音完成后回填
	},

	.hdrl_offset = 12,	//u32 hdrl_offset;
	.hdrl_len = sizeof(CHUNK_HEADER) + sizeof(AVI_HEADER) + sizeof(VIDS_STRL) + sizeof(AUDS_STRL),	//u32 hdrl_len;
	.movi_offset = sizeof(CHUNK_HEADER) * 2 + sizeof(AVI_HEADER) + sizeof(VIDS_STRL) + sizeof(AUDS_STRL),	//u32 movi_offset;
	.movi_len = 0,	//u32 movi_len;
	.idx1_offset = 0,	//u32 idx1_offset;
	.idx1_len = 0,	//u32 idx1_len;

	.TotalFrame = 0,
	.Width = VIDEO_WIDTH,
	.Height = VIDEO_HEIGHT,
	.VideoType = AVI_FORMAT_H264,
	.VideoFLAG = AVI_VIDS_FLAG0,
	.SampleRate = SAMPLERATE,
	.Channels = CHANNELS,
	.AudioType = 1,
	.AudioFLAG = AVI_AUDS_FLAG1,

	.frame_a =
	{
		.h.fccid.id = 0,
		.h.size = 0,
		.buff = 0,
		.offset = 0,
	},

	.frame_v =
	{
		.h.fccid.id = 0,
		.h.size = 0,
		.buff = 0,
		.offset = 0,
	},

};

AVI_CONTEXT* g_avi_context = 0;

VIDEO_ERROR avi_init()
{
	if (g_avi_context)
	{
		return VIDEO_ERROR_01;
	}
	g_avi_context = (AVI_CONTEXT*)MALLOC(sizeof(AVI_CONTEXT));
	if (g_avi_context == 0)
	{
		return VIDEO_ERROR_02;
	}
	memset(g_avi_context, 0, sizeof(AVI_CONTEXT));
	return VIDEO_ERROR_OK;
}


VIDEO_ERROR avi_free()
{
	if (g_avi_context)
	{
		FREE(g_avi_context);
		g_avi_context = 0;
	}
	return VIDEO_ERROR_OK;
}

JUNK* find_JUNK_v(VIDS_STRL* ch)
{
	if (ch->strh_vids.fcctype.id == FCC_vids.id)
	{
		JUNK* pJUNK = (JUNK*)(&ch->item_strf);
		for (;;)
		{
			if ((u32)pJUNK >= (u32)ch + ch->chunk_strl.size)
				break;

			if (pJUNK->h.fccid.id == FCC_JUNK.id)
			{
				if (pJUNK->fccFlagTag.id == FCC_00dc.id ||
					pJUNK->fccFlagTag.id == FCC_01dc.id)
					return pJUNK;
			}

			pJUNK = (JUNK*)((int)(pJUNK)+pJUNK->h.size + sizeof(ITEM_HEADER));
		}
	}
	else
	{
		return 0;
	}
	return 0;
}

JUNK* find_JUNK_a(AUDS_STRL* ch)
{
	if (ch->strh_auds.fcctype.id == FCC_auds.id)
	{
		JUNK* pJUNK = (JUNK*)(&ch->item_strf);
		for (;;)
		{
			if ((u32)pJUNK >= (u32)ch + ch->chunk_strl.size)
				break;

			if (pJUNK->h.fccid.id == FCC_JUNK.id)
			{
				if (pJUNK->fccFlagTag.id == FCC_00wb.id ||
					pJUNK->fccFlagTag.id == FCC_01wb.id)
					return pJUNK;
			}

			pJUNK = (JUNK*)((int)(pJUNK)+pJUNK->h.size + sizeof(ITEM_HEADER));
		}
	}
	else
	{
		return 0;
	}
	return 0;
}

AVISTATUS avi_get_avi_info(FIL* favi)
{
	FRESULT res = 0;
	u32 br = 0;

	assert(BUFF_SIZE * MEMBLOCKS > 16 * 1024);

	char* buff = (char*)mem_get_buff();
	f_lseek(favi, 0);
	res = f_read(favi, buff, 16*1024, &br);
	if (res || br != 16 * 1024)
		return AVI_FORMAT_ERR;

	AVI_FILE_MAP* map = (AVI_FILE_MAP*)buff;

	if (map->chunk_RIFF.fccid.id != FCC_RIFF.id)return AVI_RIFF_ERR;
	if (map->chunk_RIFF.fcctype.id != FCC_AVI_.id)return AVI_AVI_ERR;

	if (map->chunk_hdrl.fccid.id != FCC_LIST.id)return AVI_LIST_ERR;
	if (map->chunk_hdrl.fcctype.id != FCC_hdrl.id)return AVI_HDRL_ERR;
	if (map->avi_header.avih.fccid.id != FCC_avih.id)return AVI_AVIH_ERR;

	g_avi_context->avi_file_map = *map;

	VIDS_STRL* ch = &map->vids_strl;
	for (u32 i = 0; i < map->avi_header.mainaviheader.dwStreams; i++)
	{
		if (ch->chunk_strl.fccid.id != FCC_LIST.id)return AVI_LIST_ERR;
		if (ch->chunk_strl.fcctype.id != FCC_strl.id)return AVI_STRL_ERR;
		if (ch->strh_vids.fccid.id != FCC_strh.id)return AVI_STRL_ERR;
		if (ch->item_strf.fccid.id != FCC_strf.id)return AVI_STRF_ERR;
		if (ch->strh_vids.fcctype.id == FCC_vids.id)
		{
			g_avi_context->avi_file_map.vids_strl = *ch;
			memset(&g_avi_context->avi_file_map.vids_strl.junk, 0, sizeof(JUNK));

			JUNK* pJUNK = find_JUNK_v(ch);

			if (pJUNK)
			{
				g_avi_context->avi_file_map.vids_strl.junk = *pJUNK;
				g_avi_context->VideoFLAG = pJUNK->fccFlagTag.id;
			}
			else
			{
				g_avi_context->VideoFLAG = (i == 0) ? FCC_00dc.id : FCC_01dc.id;
			}
		}
		else if (ch->strh_vids.fcctype.id == FCC_auds.id)
		{
			g_avi_context->avi_file_map.auds_strl = *(AUDS_STRL*)ch;
			memset(&g_avi_context->avi_file_map.auds_strl.junk, 0, sizeof(JUNK));

			JUNK* pJUNK = find_JUNK_a((AUDS_STRL*)ch);

			if (pJUNK)
			{
				g_avi_context->avi_file_map.auds_strl.junk = *pJUNK;
				g_avi_context->AudioFLAG = pJUNK->fccFlagTag.id;
			}
			else
			{
				g_avi_context->AudioFLAG = (i == 0) ? FCC_00wb.id : FCC_01wb.id;
			}
		}
		else
		{
			;
		}
		ch = (VIDS_STRL*)((int)(ch)+ch->chunk_strl.size + sizeof(ITEM_HEADER));
	}

	memset(&g_avi_context->avi_file_map.chunk_movi, 0, sizeof(CHUNK_HEADER));
	memset(&g_avi_context->avi_file_map.item_idx1, 0, sizeof(ITEM_HEADER));

	g_avi_context->TotalFrame = g_avi_context->avi_file_map.avi_header.mainaviheader.dwTotalFrames;
	g_avi_context->Width = g_avi_context->avi_file_map.vids_strl.bmpinfo.biWidth;
	g_avi_context->Height = g_avi_context->avi_file_map.vids_strl.bmpinfo.biHeight;
	g_avi_context->VideoType = g_avi_context->avi_file_map.vids_strl.bmpinfo.biCompression;
	g_avi_context->SampleRate = g_avi_context->avi_file_map.auds_strl.wavformat.nSamplesPerSec;
	g_avi_context->Channels = g_avi_context->avi_file_map.auds_strl.wavformat.nChannels;
	g_avi_context->AudioType = g_avi_context->avi_file_map.auds_strl.wavformat.wFormatTag;

	g_avi_context->hdrl_offset = sizeof(CHUNK_HEADER);
	g_avi_context->hdrl_len = map->chunk_hdrl.size + 8;

	res = avi_get_movi_offset(favi);
	if (res)return res;

	res = avi_get_idx1_offset(favi);
	if (res)return res;

	res = avi_get_idx1_info(favi);
	if (res)return res;

	return AVI_OK;
}

AVISTATUS avi_get_hdrl_offset(FIL* favi)
{
	FRESULT res = 0;
	u32 br = 0;
	u32 size = sizeof(AVI_FILE_MAP);

	f_lseek(favi, 0);
	res = f_read(favi, &g_avi_context->avi_file_map, size, &br);
	if (res || br == 0)
		return AVI_FORMAT_ERR;

	AVI_FILE_MAP* map = &g_avi_context->avi_file_map;
	if (map->chunk_RIFF.fccid.id != FCC_RIFF.id)return AVI_RIFF_ERR;
	if (map->chunk_RIFF.fcctype.id != FCC_AVI_.id)return AVI_AVI_ERR;

	if (map->chunk_hdrl.fccid.id != FCC_LIST.id)return AVI_LIST_ERR;
	if (map->chunk_hdrl.fcctype.id != FCC_hdrl.id)return AVI_HDRL_ERR;

	g_avi_context->hdrl_offset = sizeof(CHUNK_HEADER);
	g_avi_context->hdrl_len = map->chunk_hdrl.size + 8;

	return AVI_OK;
}

AVISTATUS avi_get_movi_offset(FIL* favi)
{
	FRESULT res = 0;
	u32 br = 0;
	CHUNK_HEADER chunk;
	u32 size = sizeof(CHUNK_HEADER);
	FSIZE_t offset = g_avi_context->hdrl_offset + g_avi_context->hdrl_len;

	while (1)
	{
		f_lseek(favi, offset);
		res = f_read(favi, &chunk, size, &br);
		if (res || br != size)
			return AVI_FORMAT_ERR;

		if (chunk.fccid.id == FCC_LIST.id)
		{
			if (chunk.fcctype.id == FCC_movi.id)
			{
				g_avi_context->avi_file_map.chunk_movi = chunk;
				g_avi_context->movi_offset = offset;
				g_avi_context->movi_len = chunk.size + 8;
				return AVI_OK;
			}

			offset += chunk.size + 8;
		}
		else
		{
			offset += chunk.size + 8;
		}
	}

	return AVI_OK;
}

AVISTATUS avi_get_idx1_offset(FIL* favi)
{
	FRESULT res = 0;
	u32 br = 0;
	CHUNK_HEADER chunk;
	u32 size = sizeof(CHUNK_HEADER);
	FSIZE_t offset = g_avi_context->movi_offset + g_avi_context->movi_len;

	while (1)
	{
		f_lseek(favi, offset);
		res = f_read(favi, &chunk, size, &br);
		if (res || br < 8)
			return AVI_FORMAT_ERR;

		if (chunk.fccid.id == FCC_LIST.id)
		{
			offset += chunk.size + 8;
		}
		else
		{
			if (chunk.fccid.id == FCC_idx1.id)
			{
				g_avi_context->avi_file_map.item_idx1 = *(ITEM_HEADER*)(&chunk);
				g_avi_context->idx1_offset = offset;
				g_avi_context->idx1_len = chunk.size + 8;
				return AVI_OK;
			}
			else
			{
				offset += chunk.size + 8;
			}
		}
	}

	return AVI_OK;
}

AVISTATUS avi_get_idx1_info(FIL* favi)
{
	favi = favi;
	return AVI_OK;
}

AVISTATUS avi_seek_to_movi(FIL* favi)
{
	FRESULT res = 0;
	res = f_lseek(favi, g_avi_context->movi_offset + sizeof(CHUNK_HEADER));
	if (res)	return AVI_FORMAT_ERR;
	return AVI_OK;
}


VIDEO_ERROR avi_get_block_head(FIL* favi, ITEM_HEADER* h)
{
	FRESULT res = 0;
	u32 br = 0;

	assert(h);

	res = f_read(favi, h, sizeof(ITEM_HEADER), &br);
	if (res || br != sizeof(ITEM_HEADER))
	{
		return VIDEO_ERROR_99;
	}

	//if (h->size % 2) h->size++;

	switch (h->fccid.id)
	{
	case AVI_AUDS_FLAG0://audio
	case AVI_AUDS_FLAG1://audio
	case AVI_VIDS_FLAG0://video
	case AVI_VIDS_FLAG1://video
		return VIDEO_ERROR_OK;

	case AVI_IX00_ID:
	case AVI_IDX1_ID:
		return VIDEO_ERROR_99;

	default://unknow tag
		return VIDEO_ERROR_09;
	}
}

VIDEO_ERROR avi_get_block_data(FIL* favi, void* data, u32 len)
{
	FRESULT res = 0;
	u32 br = 0;

	assert(data);

	if (len % 2)
		len++;

	res = f_read(favi, data, len, &br);
	if (res || br != len)
	{
		return VIDEO_ERROR_99;
	}
	return VIDEO_ERROR_OK;
}

void avi_update_file_map(FIL* favi)
{
	g_avi_context->avi_file_map.chunk_RIFF.size = (u32)(f_size(favi) - 8);
	g_avi_context->avi_file_map.chunk_movi.size = (u32)(g_avi_context->idx1_offset - g_avi_context->movi_offset - 8);
	//g_avi_context->avi_file_map.chunk_movi.size = (u32)(f_size(favi) - g_avi_context->movi_offset - 8);
	g_avi_context->avi_file_map.item_idx1.size = 0;
}

void avi_config()
{
	*g_avi_context = default_conxext;
}

VIDEO_ERROR avi_put_avi_info(FIL* favi)
{
	VIDEO_ERROR res = 0;
	u32 bw = 0;
	u32 len = (u32)(&((AVI_CONTEXT*)0)->avi_file_map.item_idx1);

	res = f_lseek(favi, 0);
	if (res)	return AVI_FORMAT_ERR;

	res = f_write(favi, &g_avi_context->avi_file_map, len, &bw);
	if (res || bw != len)
	{
		return VIDEO_ERROR_99;
	}

	return VIDEO_ERROR_OK;
}

VIDEO_ERROR avi_put_block_head(FIL* favi, ITEM_HEADER* h)
{
	FRESULT res = 0;
	u32 bw = 0;

	assert(h);

	res = f_write(favi, h, sizeof(ITEM_HEADER), &bw);
	if (res || bw != sizeof(ITEM_HEADER))
	{
		return VIDEO_ERROR_17;
	}

	f_sync(favi);

	return VIDEO_ERROR_OK;
}

VIDEO_ERROR avi_put_block_data(FIL* favi, void* data, u32 len)
{
	FRESULT res = 0;
	u32 bw = 0;

	assert(data);

	res = f_write(favi, data, len, &bw);
	if (res || bw != len)
	{
		return VIDEO_ERROR_17;
	}

	f_sync(favi);

	return VIDEO_ERROR_OK;
}

static VIDEO_ERROR avi_parse_idx_node(u8* buff, int len, FSIZE_t ofs, AVIINDEXENTRY* node)
{
	ITEM_HEADER* head = (ITEM_HEADER*)buff;

	node->ckid = head->fccid.id;
	node->dwChunkOffset = (DWORD)(ofs - g_avi_context->movi_offset - sizeof(ITEM_HEADER));
	node->dwChunkLength = head->size;

	switch (node->ckid)
	{
	case AVI_VIDS_FLAG0:
	case AVI_VIDS_FLAG1:
		if (1/*I_FRAME_BUF*/ == H264_GetAnnexbNALU(buff + sizeof(ITEM_HEADER), len - sizeof(ITEM_HEADER)))
		{
			node->dwFlags = AVIIF_KEYFRAME;
		}
		else
		{
			node->dwFlags = 0;
		}
		break;

	case AVI_AUDS_FLAG0:
	case AVI_AUDS_FLAG1:
		node->dwFlags = AVIIF_KEYFRAME;
		break;

	default:
		return VIDEO_ERROR_09;
	}

	return VIDEO_ERROR_OK;
}

VIDEO_ERROR avi_put_idx1_info(FIL* favi)
{
	FRESULT res = 0;
	u32 bw = sizeof(ITEM_HEADER);

	g_avi_context->idx1_offset = f_tell(favi);

	res = f_write(favi, &g_avi_context->avi_file_map.item_idx1, bw, &bw);
	if (res || bw != sizeof(ITEM_HEADER))
	{
		return VIDEO_ERROR_17;
	}

	f_sync(favi);

	FSIZE_t ofs_current = g_avi_context->movi_offset + sizeof(CHUNK_HEADER);

	for (;;)
	{
		if (ofs_current >= g_avi_context->idx1_offset)
			break;

		f_lseek(favi, ofs_current);

		AVIINDEXENTRY node = { 0 };
		u8 buff[sizeof(ITEM_HEADER) + 128] = { 0 };
		res = f_read(favi, buff, sizeof(ITEM_HEADER) + 128, &bw);
		if (res || bw != sizeof(ITEM_HEADER) + 128)
		{
			return VIDEO_ERROR_07;
		}
		res = avi_parse_idx_node(buff, sizeof(ITEM_HEADER) + 128, ofs_current, &node);
		if (res)return res;

		f_lseek(favi, f_size(favi));
		res = f_write(favi, &node, sizeof(AVIINDEXENTRY), &bw);
		if (res || bw != sizeof(AVIINDEXENTRY))
		{
			return VIDEO_ERROR_17;
		}
		f_sync(favi);
		
		ofs_current += sizeof(ITEM_HEADER) + node.dwChunkLength;
		if (ofs_current % 2)ofs_current++;
	}

	f_sync(favi);

	g_avi_context->avi_file_map.item_idx1.size = (u32)(f_size(favi) - g_avi_context->idx1_offset - 8);
	f_lseek(favi, g_avi_context->idx1_offset);
	res = f_write(favi, &g_avi_context->avi_file_map.item_idx1, sizeof(ITEM_HEADER), &bw);
	if (res || bw != sizeof(ITEM_HEADER))
	{
		return VIDEO_ERROR_17;
	}
	return VIDEO_ERROR_OK;
}

VIDEO_ERROR avi_ensure_alignment(FIL* favi)
{
	FRESULT res = 0;
	u32 bw = 0;
	char padding = 0;
	u32 size = (u32)f_size(favi);

	if (size % 2)
	{
		res = f_write(favi, &padding, 1, &bw);
		if (res || bw != 1)
		{
			return VIDEO_ERROR_17;
		}
	}

	return VIDEO_ERROR_OK;
}
